{
  This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
  the Free Software Foundation; version 2 of the License.
   
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
}

// Copyright (c) 2010 2011 2012 - J. Aldo G. de Freitas Junior

{$mode objfpc}
{$H+}

Unit
	XMLTemplate;

Interface

Uses
	Classes,
	SysUtils,
	StrUtils,
	BaseException,
	Tree,
	NameValue,
	Stacks,
	XMLNodes,
	XMLLoader,
	ExprNodes,
	ExprLoader,
	XMLParser,
	XMLScanner;

Const
	ccNewLine = '\n';
	ccSpace = '\s';
	ccTab = '\t';

Type
	TXMLTemplateNode = Class(TXMLNode)
	Private
		fStack : TVirtualMachineStack;
	Public
		Constructor Create(Const aOwner : TTreeNode); Override;
		Class Function RegisteredName : String; Override;
		Function AsXML: String; Override;
		Function AsClosingXML: String; Override;
		Function Emit: String; Virtual;
		Function EmitChilds: String; Virtual;
		Procedure PropagateStack(Const aStack : TVirtualMachineStack); Virtual;
		Property Stack : TVirtualMachineStack Read fStack Write fStack;
	End;

	TXMLTemplateRootNode = Class(TXMLTemplateNode)
	Public
		Class Function RegisteredName : String; Override;
		Procedure LoadFromFile(Const aFileName : String);
	End;

	TXMLWith = Class(TXMLTemplateNode)
	Private
		fNode : String;
	Public
		Class Function RegisteredName : String; Override;
		Function Emit: String; Override;
	Published
		Property Node : String Read fNode Write fNode;
	End;

	TXMLTemplateIf = Class(TXMLTemplateNode)
	Private
		fExpression : String;
	Public
		Constructor Create(Const aOwner : TTreeNode); Override;
		Class Function RegisteredName : String; Override;
		Function Emit: String; Override;
	Published
		Property Expression : String Read fExpression Write fExpression;
	End;
	
	TXMLTemplateThen = Class(TXMLTemplateNode)
	Public
		Class Function RegisteredName : String; Override;
	End;
	
	TXMLTemplateElse = Class(TXMLTemplateNode)
	Public
		Class Function RegisteredName : String; Override;
	End;

	TXMLTemplateForEach = Class(TXMLTemplateNode)
	Private
		fNode : String;
	Public
		Class Function RegisteredName : String; Override;
		Function Emit: String; Override;
	Published
		Property Node : String Read fNode Write fNode;
	End;

	TXMLGetConfig = Class(TXMLTemplateNode)
	Private
		fNode,
		fProperty : String;
	Public
		Constructor Create(Const aOwner : TTreeNode); Override;
		Class Function RegisteredName : String; Override;
		Function Emit: String; Override;
	Published
		Property Node : String Read fNode Write fNode;
		Property PropertyName : String Read fProperty Write fProperty;
	End;

	TXMLGetText = Class(TXMLTemplateNode)
	Private
		fNode : String;
	Public
		Constructor Create(Const aOwner : TTreeNode); Override;
		Class Function RegisteredName : String; Override;
		Function Emit: String; Override;
	Published
		Property Node : String Read fNode Write fNode;
	End;
	
	TXMLOutText = Class(TXMLTemplateNode)
	Private
		fExpression : String;
	Public
		Constructor Create(Const aOwner : TTreeNode); Override;
		Class Function RegisteredName : String; Override;
		Function Emit: String; Override;
	Published
		Property Expression : String Read fExpression Write fExpression;
	End;

Implementation

// TXMLTemplateNode

Constructor TXMLTemplateNode.Create(Const aOwner : TTreeNode);
Begin
	Inherited Create(aOwner);
	Factory.Register(TXMLWith);
	Factory.Register(TXMLTemplateIf);
	Factory.Register(TXMLTemplateForEach);
	Factory.Register(TXMLGetConfig);
	Factory.Register(TXMLGetText);
	Factory.Register(TXMLOutText);
End;

Class Function TXMLTemplateNode.RegisteredName : String;
Begin
	RegisteredName := 'templatenode';
End;

Function TXMLTemplateNode.AsXML: String;
Begin
	Result := '';
End;

Function TXMLTemplateNode.AsClosingXML: String;
Begin
	Result := '';
End;

Function TXMLTemplateNode.Emit: String;
Begin
	// Debug   WriteLn(Source, ' start emit.');
	If Length(Childs) > 0 Then
		Result := AsXML + EmitChilds + AsClosingXML
	Else
		Result := AsXML;
	// Debug   WriteLn(Source, ' end emit.');
End;

Function TXMLTemplateNode.EmitChilds: String;
Begin
	First;
	While Not IsAfterLast Do
	Begin
		If Assigned(GetCurrent) And (GetCurrent Is TXMLTextNode) Then
			Result := Result + (GetCurrent As TXMLTextNode).Content
		Else If Assigned(GetCurrent) And (GetCurrent Is TXMLTemplateNode) Then
			Result := Result + (GetCurrent As TXMLTemplateNode).Emit;
		Next;
	End;
End;

Procedure TXMLTemplateNode.PropagateStack(Const aStack : TVirtualMachineStack);
Var
	lSave : Integer;
Begin
	fStack := aStack;
	lSave := GetCurrentIndex;
	First;
	While Not IsAfterLast Do
	Begin
		If GetCurrent Is TXMLTemplateNode Then
			(GetCurrent As TXMLTemplateNode).PropagateStack(aStack);
		Next;
	End;
	SetcurrentIndex(lSave);
End;

// TXMLTemplateRootNode

Class Function TXMLTemplateRootNode.RegisteredName : String;
Begin
	Result := '$root$';
End;

Procedure TXMLTemplateRootNode.LoadFromFile(Const aFileName : String);
Var
	lFile : TFileStream;
	lSource : TXMLSource;
	lScanner : TXMLScanner;
	lTokenIterator : TXMLTokenIterator;
	lParser : TXMLParser;
Begin
	lFile := Nil;
	lSource := Nil;
	lScanner := Nil;
	lTokenIterator := Nil;
	lParser := Nil;
	Try
		Row := 1;
		Col := 1;
		Source := aFileName;
		lFile := TFileStream.Create(aFileName, fmOpenRead);
		lSource := TXMLSource.Create(aFileName, lFile);
		lScanner := TXMLScanner.Create(lSource);
		lScanner.Scan;
		lTokenIterator := TXMLTokenIterator.Create(lScanner.Destination);
		lParser := TXMLParser.Create(lTokenIterator, Self);
		lParser.Parse;
		PropagateStack(Stack);
	Finally
		FreeAndNil(lFile);
		FreeAndNil(lSource);
		FreeAndNil(lScanner);
		FreeAndNil(lTokenIterator);
		FreeAndNil(lParser);
	End;
End;

// TXMLWith 

Class Function TXMLWith.RegisteredName : String;
Begin
	Result := 'with';
End;

Function TXMLWith.Emit: String;
Var
	lLoader : TExprRootNode;
	lSave : TXMLNode;
Begin
	// Debug   WriteLn(IndexedName, ' start emit.');
	lSave := Stack.Focused;
	lLoader := TExprRootNode.Create(Nil);
	Try
		lLoader.DoLoadDomainName(Source, fNode);
		lloader.PropagateStack(Stack);
		Try
			lLoader.Evaluate;
			Result := Result + EmitChilds;
		Except
			On E: Exception Do
				Raise EInterpreterException.Create(Row, Col, Source, E);
		End;
	Finally
		FreeAndNil(lLoader);
		Stack.Focused := lSave;
	End;
	// Debug   WriteLn(IndexedName, ' end emit.');
End;

// TXMLTemplateIf

Constructor TXMLTemplateIf.Create(Const aOwner : TTreeNode);
Begin
	Inherited Create(aOwner);
	Factory.Clear;
	Factory.Register(TXMLTemplateThen);
	Factory.Register(TXMLTemplateElse);
End;

Class Function TXMLTemplateIf.RegisteredName : String;
Begin
	Result := 'if';
End;

Function TXMLTemplateIf.Emit: String;
Var
	lExpression : TExprRootNode;
Begin
	// Debug   WriteLn(IndexedName, ' start emit.');
	Try
		lExpression := TExprRootNode.Create(Nil);
		lExpression.DoLoadExpression(Source, fExpression);
		lExpression.PropagateStack(Stack);
		Stack.Enter(0);
		Try
			lExpression.Evaluate;
		Except
			On E: Exception Do
				Raise EInterpreterException.Create(Row, Col, Source, E);
		End;
		If Stack.Top.Pop = True Then
			If HasChildNamed('then') Then
				Result := (Find('then') As TXMLTemplateNode).EmitChilds
			Else
		Else
			If HasChildNamed('else') Then
				Result := (Find('else') As TXMLTemplateNode).EmitChilds;
		Stack.Leave(0);
		// Debug   WriteLn(IndexedName, ' end emit.');
	Finally
		FreeAndNil(lExpression);
	End;
End;

// TXMLTemplateThen

Class Function TXMLTemplateThen.RegisteredName : String;
Begin
	Result := 'then';
End;

// TXMLTemplateElse

Class Function TXMLTemplateElse.RegisteredName : String;
Begin
	Result := 'else';
End;

// TXMLTemplateForEach

Class Function TXMLTemplateForEach.RegisteredName : String;
Begin
	Result := 'foreach';
End;

Function TXMLTemplateForEach.Emit: String;
Var
	lLoader : TExprRootNode;
	lSave : TXMLNode;
	lIterate : TXMLNode;
Begin
	// Debug   WriteLn(IndexedName, ' start emit.');
	lSave := Stack.Focused;
	lLoader := TExprRootNode.Create(Nil);
	Try
		lLoader.DoLoadDomainName(Source, fNode);
		lLoader.PropagateStack(Stack);
		Try
			lLoader.Evaluate;
		Except
			On E: Exception Do
				Raise EInterpreterException.Create(Row, Col, Source, E);
		End;
		Stack.Focused.First;
		While Not Stack.Focused.IsAfterLast Do
		Begin
			lIterate := Stack.Focused;
			Result := Result + Inherited EmitChilds;
			Stack.Focused := lIterate;
			Stack.Focused.Next;
		End;
		// Debug   WriteLn(IndexedName, ' end emit.');
	Finally
		Stack.Focused := lSave;
		FreeAndNil(lLoader);
	End;	
End;

// TXMLGetConfig

Constructor TXMLGetConfig.Create(Const aOwner : TTreeNode);
Begin
	Inherited Create(aOwner);
	Factory.Clear;
End;

Class Function TXMLGetConfig.RegisteredName : String;
Begin
	Result := 'get';
End;

Function TXMLGetConfig.Emit: String;
Begin
	// Debug   WriteLn(IndexedName, ' start emit.');
	Result := Stack.Focused.Properties[fProperty];
	// Debug   WriteLn(IndexedName, ' end emit.');
End;

// TXMLGetText

Constructor TXMLGetText.Create(Const aOwner : TTreeNode);
Begin
	Inherited Create(aOwner);
	Factory.Clear;
End;

Class Function TXMLGetText.RegisteredName : String;
Begin
	Result := 'gettext';
End;

Function TXMLGetText.Emit: String;
Begin
	// Debug   WriteLn(IndexedName, ' start emit.');
	If Stack.Focused.HasTextChild Then
		Result := Stack.Focused.GetTextChild
	Else
		RaiseError('Node has no text.');
	// Debug   WriteLn(IndexedName, ' end emit.');
End;

// TXMLOutText

Constructor TXMLOutText.Create(Const aOwner : TTreeNode);
Begin
	Inherited Create(aOwner);
	Factory.Clear;
End;

Class Function TXMLOutText.RegisteredName : String;
Begin
	Result := 'out';
End;

Function TXMLOutText.Emit: String;
Var
	lExpression : TExprRootNode;
Begin
	// Debug   WriteLn(IndexedName, ' start emit.');
	Try
		lExpression := TExprRootNode.Create(Nil);
		lExpression.DoLoadExpression(Source, fExpression);
		lExpression.PropagateStack(Stack);
		Stack.Enter(0);
		Try
			lExpression.Evaluate;
		Except
			On E: Exception Do
				Raise EInterpreterException.Create(Row, Col, Source, E);
		End;
		Result := Stack.Top.Pop;
		Stack.Leave(0);
		// Debug   WriteLn(IndexedName, ' end emit.');
	Finally
		FreeAndNil(lExpression);
	End;
End;

End.
